﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Xml;
using System.IO;
using SimpleText.Framework;
using System.Web.UI.WebControls;
using System.Text;
using System.Text.RegularExpressions;

/// <summary>
/// Base class for all ASP.NET pages in the site
/// </summary>
public class SimpleTextPage : System.Web.UI.Page 
{
	protected LanguageVersioningModel CurrentModel { get; set; }
	protected string CurrentProjectFilePathAndName {get; set;}

	public void InitPage()
	{
		if ( ! String.IsNullOrEmpty(Request.QueryString["setproject"]) )
		{
			SetCurrentModel(Request.QueryString["setproject"]);
			Response.Redirect("~");
		}
		CurrentModel = GetCurrentModel();


		if (Master != null)
		{
			//Current project
			Literal CurrentProjectNameLit = (Literal) Master.FindControl("CurrentProjectNameLit");
			Literal CurrentProjectPathLit = (Literal)Master.FindControl("CurrentProjectPathLit");
			Literal CurrentProjectShortenedPathLit = (Literal)Master.FindControl("CurrentProjectShortenedPathLit");
			PlaceHolder CurrentProjectPlh = (PlaceHolder) Master.FindControl("CurrentProjectPlh");

			if (CurrentModel != null)
			{
				CurrentProjectPlh.Visible = true;
				if (CurrentProjectNameLit != null)
				{
					CurrentProjectNameLit.Text = CurrentModel.ProjectName;
				}
				if (CurrentProjectPathLit != null)
				{
					CurrentProjectPathLit.Text = CurrentModel.LoadedFromXmlFilePath;
				}
				if (CurrentProjectShortenedPathLit != null)
				{
					CurrentProjectShortenedPathLit.Text = "<span title=\"" + CurrentModel.LoadedFromXmlFilePath + "\">" + SimpleTextPage.ShortenPath(CurrentModel.LoadedFromXmlFilePath, 50) + "</span>";
				}
			}
			else
			{
				CurrentProjectPlh.Visible = false;
			}
				
			//List available projects
			//local
			Repeater localProjRep = (Repeater) Master.FindControl("LocalProjectsRpt");
			if (localProjRep != null)
			{
				string[] filePaths = Directory.GetFiles(Server.MapPath("~/App_Data/Projects"));
				if (filePaths.Length == 0)
				{
					PlaceHolder NoLocalProjectsPlh = (PlaceHolder)Master.FindControl("NoLocalProjectsPlh");
					PlaceHolder LocalProjectsPlh = (PlaceHolder)Master.FindControl("LocalProjectsPlh");
					LocalProjectsPlh.Visible = false;
					NoLocalProjectsPlh.Visible = true;
				}
				else
				{
					List<ProjectSummary> projects = new List<ProjectSummary>();
					foreach (string filePath in filePaths)
					{
						if (File.Exists(filePath))
						{
							ProjectSummary summaryToAdd = new ProjectSummary();
							summaryToAdd.FileFullPath = filePath;
							summaryToAdd.ProjectName = GetProjectName(filePath);
							projects.Add(summaryToAdd);
						}
					}
					localProjRep.DataSource = projects;
					localProjRep.DataBind();
				}
			}

			//External
			Repeater externalProjRep = (Repeater)Master.FindControl("ExternalProjectsRpt");
			if (externalProjRep != null)
			{
				string[] filePaths = GetRecentProjectExternalFilePaths();
				if (filePaths.Length == 0)
				{
					PlaceHolder NoExternalProjectsPlh = (PlaceHolder)Master.FindControl("NoExternalProjectsPlh");
					PlaceHolder ExternalProjectsPlh = (PlaceHolder)Master.FindControl("ExternalProjectsPlh");
					ExternalProjectsPlh.Visible = false;
					NoExternalProjectsPlh.Visible = true;
				}
				else
				{
					List<ProjectSummary> projects = new List<ProjectSummary>();
					foreach (string filePath in filePaths)
					{
						if (File.Exists(filePath))
						{
							ProjectSummary summaryToAdd = new ProjectSummary();
							summaryToAdd.FileFullPath = filePath;
							summaryToAdd.ProjectName = GetProjectName(filePath);
							projects.Add(summaryToAdd);
						}
					}
					externalProjRep.DataSource = projects;
					externalProjRep.DataBind();
				}
			}

		}
	}

	protected string GetProjectName(string filePath)
	{
		string cacheKey = "projectname_" + filePath;
		string projectName = (string) Cache[cacheKey];
		if (projectName == null)
		{
			//ToDo: Should use sequential XML reader instead, so it does not need to read the entire file from disk (projectName is among the very first elements in the file)
			XmlDocument xmlDoc = new XmlDocument();
			Trace.Write("filePath", "/" + filePath + "/");
			xmlDoc.Load(filePath);
			XmlNode rootNode = xmlDoc.SelectSingleNode("languageversioning");
			if (rootNode == null)
			{
				projectName = "";
			}
			else
			{
				XmlAttribute projectNameAttr = rootNode.Attributes["projectName"];
				if (projectNameAttr == null)
				{
					projectName = "";
				}
				else
				{
					projectName = projectNameAttr.Value;
				}
			}
			Cache.Insert(cacheKey, projectName, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration);
		}

		return projectName;
	}
	protected void SetCurrentModel(string filePath)
	{
		LanguageVersioningModel model = null;

		model = LoadModel(filePath);

		CurrentModel = model;
		Session["CurrentModel"] = model;
		SetCookieValue("CurrentModelFilePath", filePath, 30);
	}

	protected string GetClassNamesForCssSprite(string cultureCode)
	{
		if (cultureCode == "-")
		{
			//should never occur, but can be set as a result of failed validation ("-" is the "nothing selected" value in the drop-down)
			return "";
		}
		if (cultureCode.IndexOf('-') == -1)
		{
			//this is probably only a language code (e.g. da instead of da-DK)
			return "";
		}
		string countryCode = cultureCode.Substring(cultureCode.IndexOf('-') + 1, 2);
		return "flag " + countryCode.ToLower();
	}
	
	protected LanguageVersioningModel GetCurrentModel()
	{
		LanguageVersioningModel model = null;
		if (Session["CurrentModel"] != null)
		{
			//project already loaded in session
			model = (LanguageVersioningModel)Session["CurrentModel"];
		}
		else if (GetCookieValue("CurrentModelFilePath") != null)
		{
			//persisted in a cookie
			model = LoadModel(GetCookieValue("CurrentModelFilePath"));
		}

		return model;
	}
	
	protected string[] GetRecentProjectExternalFilePaths()
	{
		string[] externalFilePaths = new string[0];
		string externalFilePathsString = GetCookieValue("RecentExternalProjectFilePaths");

		if (externalFilePathsString != null)
		{
			externalFilePaths = externalFilePathsString.Split('*');
		}

		return externalFilePaths;
	}
	protected void AddRecentProjectExternalFilePath(string path)
	{
		string[] currentPaths = GetRecentProjectExternalFilePaths();

		List<string> newPaths = new List<string>();
		
		//paths which are new or already in the list are moved to the top
		newPaths.Add(path);
		
		int length = 0;
		foreach (string currentPath in currentPaths)
		{
			if (currentPath != path)
			{
				length += currentPath.Length;
				if (length > 4000)
				{
					//4000 chars is safe cookie max value across most browsers (http://manicode.blogspot.com/2009/08/real-world-cookie-length-limits.html)
					break;
				}
				newPaths.Add(currentPath);
			}
		}
		SetCookieValue("RecentExternalProjectFilePaths", String.Join("*", newPaths.ToArray()), 30);
	}


	/// <summary>
	/// Shortens a file path to the specified length
	/// </summary>
	/// <param name="path">The file path to shorten</param>
	/// <param name="maxLength">The max length of the output path (including the ellipsis if inserted)</param>
	/// <returns>The path with some of the middle directory paths replaced with an ellipsis (or the entire path if it is already shorter than maxLength)</returns>
	/// <remarks>
	/// Shortens the path by removing some of the "middle directories" in the path and inserting an ellipsis. If the filename and root path (drive letter or UNC server name) in itself exceeds the maxLength, the filename will be cut to fit.
	/// UNC-paths and relative paths are also supported.
	/// The inserted ellipsis is not a true ellipsis char, but a string of three dots.
	/// </remarks>
	/// <example>
	/// ShortenPath(@"c:\websites\myproject\www_myproj\App_Data\themegafile.txt", 50)
	/// Result: "c:\websites\myproject\...\App_Data\themegafile.txt"
	/// 
	/// ShortenPath(@"c:\websites\myproject\www_myproj\App_Data\theextremelylongfilename_morelength.txt", 30)
	/// Result: "c:\...gfilename_morelength.txt"
	/// 
	/// ShortenPath(@"\\myserver\theshare\myproject\www_myproj\App_Data\theextremelylongfilename_morelength.txt", 30)
	/// Result: "\\myserver\...e_morelength.txt"
	/// 
	/// ShortenPath(@"\\myserver\theshare\myproject\www_myproj\App_Data\themegafile.txt", 50)
	/// Result: "\\myserver\theshare\...\App_Data\themegafile.txt"
	/// 
	/// ShortenPath(@"\\192.168.1.178\theshare\myproject\www_myproj\App_Data\themegafile.txt", 50)
	/// Result: "\\192.168.1.178\theshare\...\themegafile.txt"
	/// 
	/// ShortenPath(@"\theshare\myproject\www_myproj\App_Data\", 30)
	/// Result: "\theshare\...\App_Data\"
	/// 
	/// ShortenPath(@"\theshare\myproject\www_myproj\App_Data\themegafile.txt", 35)
	/// Result: "\theshare\...\themegafile.txt"
	/// </example>
	public static string ShortenPath(string path, int maxLength)
	{
		string ellipsisChars = "...";
		char dirSeperatorChar = Path.DirectorySeparatorChar;
		string directorySeperator = dirSeperatorChar.ToString();

		//simple guards
		if (path.Length <= maxLength)
		{
			return path;
		}
		int ellipsisLength = ellipsisChars.Length;
		if (maxLength <= ellipsisLength)
		{
			return ellipsisChars;
		}


		//alternate between taking a section from the start (firstPart) or the path and the end (lastPart)
		bool isFirstPartsTurn = true; //drive letter has first priority, so start with that and see what else there is room for

		//vars for accumulating the first and last parts of the final shortened path
		string firstPart = "";
		string lastPart = "";
		//keeping track of how many first/last parts have already been added to the shortened path
		int firstPartsUsed = 0;
		int lastPartsUsed = 0;

		string[] pathParts = path.Split(dirSeperatorChar);
		for (int i = 0; i < pathParts.Length; i++)
		{
			if (isFirstPartsTurn)
			{
				string partToAdd = pathParts[firstPartsUsed] + directorySeperator;
				if ((firstPart.Length + lastPart.Length + partToAdd.Length + ellipsisLength) > maxLength)
				{
					break;
				}
				firstPart = firstPart + partToAdd;
				if (partToAdd == directorySeperator)
				{
					//this is most likely the first part of and UNC or relative path 
					//do not switch to lastpart, as these are not "true" directory seperators
					//otherwise "\\myserver\theshare\outproject\www_project\file.txt" becomes "\\...\www_project\file.txt" instead of the intended "\\myserver\...\file.txt")
				}
				else
				{
					isFirstPartsTurn = false;
				}
				firstPartsUsed++;
			}
			else
			{
				int index = pathParts.Length - lastPartsUsed - 1; //-1 because of length vs. zero-based indexing
				string partToAdd = directorySeperator + pathParts[index];
				if ((firstPart.Length + lastPart.Length + partToAdd.Length + ellipsisLength) > maxLength)
				{
					break;
				}
				lastPart = partToAdd + lastPart;
				if (partToAdd == directorySeperator)
				{
					//this is most likely the last part of a relative path (e.g. "\websites\myproject\www_myproj\App_Data\")
					//do not proceed to processing firstPart yet
				}
				else
				{
					isFirstPartsTurn = true;
				}
				lastPartsUsed++;
			}
		}
		
		if (lastPart == "")
		{
			//the filename (and root path) in itself was longer than maxLength, shorten it
			lastPart = pathParts[pathParts.Length - 1];//"pathParts[pathParts.Length -1]" is the equivalent of "Path.GetFileName(pathToShorten)"
			lastPart = lastPart.Substring(lastPart.Length + ellipsisLength + firstPart.Length - maxLength, maxLength - ellipsisLength - firstPart.Length);
		}

		return firstPart + ellipsisChars + lastPart;
	}

	protected string GetCookieValue(string cookieName)
	{
		HttpCookie cookie = Request.Cookies[cookieName];
		if (cookie == null)
		{
			return null;
		}
		else
		{
			return cookie.Value;
		}
	}
	protected void SetCookieValue(string cookieName,string cookieValue, int daysToLive)
	{
		if (Response.Cookies[cookieName] == null)
		{
			HttpCookie newCookie = new HttpCookie(cookieName, cookieValue);
			newCookie.Expires = DateTime.Now.AddDays(daysToLive);
			Response.Cookies.Add(newCookie);
		}
		else
		{
			Response.Cookies[cookieName].Value = cookieValue;
			Response.Cookies[cookieName].Expires = DateTime.Now.AddDays(daysToLive);
		}
	}

	public LanguageVersioningModel LoadModel(string xmlFileFullPath)
	{

		if (!File.Exists(xmlFileFullPath))
		{
			//probably because file has been removed since cookie was written
			return null;
		}

		XmlDocument xmlDoc = new XmlDocument();
		xmlDoc.Load(xmlFileFullPath);

		//schema was changed in v0.11 - check if this is an old version
		if (xmlDoc.SelectSingleNode("//textDefinitionGroups") != null)
		{
			SetCookieValue("CurrentModelFilePath", "", 30);
			Session["CurrentModel"] = null;
			Response.Redirect("legacy_convertproject.aspx?projectfile=" + Server.UrlEncode(xmlFileFullPath));
		}

		LanguageVersioningModel model = new LanguageVersioningModel(xmlDoc);
		model.LoadedFromXmlFilePath = xmlFileFullPath;
		Session["CurrentModel"] = model;

		return model;
	}
	public void SaveModel(LanguageVersioningModel modelToSave, string xmlFileFullPath)
	{
		File.WriteAllText(xmlFileFullPath, modelToSave.GetAsXml());
		Session["CurrentModel"] = modelToSave;
	}

	public string LimitLength(string input, int maxLength)
	{
		if (input == null)
		{
			return "";
		}
		if (input.Length <= maxLength)
		{
			return input;
		}
		return input.Substring(0, maxLength -3) + "...";

	}

	public string GetFormattedFileSize(long Bytes)
	{
		if (Bytes >= 1073741824)
		{
			Decimal size = Decimal.Divide(Bytes, 1073741824);
			return String.Format("{0:##.##} GB", size);
		}
		else if (Bytes >= 1048576)
		{
			Decimal size = Decimal.Divide(Bytes, 1048576);
			return String.Format("{0:##.##} MB", size);
		}
		else if (Bytes >= 1024)
		{
			Decimal size = Decimal.Divide(Bytes, 1024);
			return String.Format("{0:##.##} KB", size);
		}
		else if (Bytes > 0 & Bytes < 1024)
		{
			Decimal size = Bytes;
			return String.Format("{0:##.##} Bytes", size);
		}
		else
		{
			return "0 Bytes";
		}
	}

	public List<TextDefinitionFoundByWizard> ExtractTextsFromFile(string fullPathAndFileName, string regexPattern)
	{
		List<TextDefinitionFoundByWizard> foundTexts = new List<TextDefinitionFoundByWizard>();

		string fileContent = File.ReadAllText(fullPathAndFileName);
		//string regex = "Txt.Get[\w]*\(\"(?<definitionmatch>([\w]*))\"\)(\s*/[\*]{3}(?<defaulttextmatch>(.|\s)*?)[\*]{3}/)?";
		string regex = regexPattern;
		MatchCollection textMatches = Regex.Matches(fileContent, regex, RegexOptions.IgnoreCase | RegexOptions.Compiled);

		foreach (Match textMatch in textMatches)
		{
			Group definitionGroup = textMatch.Groups["definitionmatch"];
			Group defaultValueGroup = textMatch.Groups["defaulttextmatch"];
			string matchedDefinitionName = definitionGroup.Value;
			string defaultValue = defaultValueGroup.Value;

			TextDefinitionFoundByWizard foundText = new TextDefinitionFoundByWizard();
			foundText.TextDefinitionName = matchedDefinitionName;
			foundText.DefaultText = defaultValue.Trim();

			foundTexts.Add(foundText);
		}

		return foundTexts;
	}

	public List<TextGroupFoundByWizard> ExtractTextsFromFiles(string baseFolder, string[] extensionsToInclude, List<TextGroupFoundByWizard> foundTextGroups, string pathToSearch, string regexPattern)
	{
		string rootPath = pathToSearch;

		string[] files = Directory.GetFiles(baseFolder);
		foreach (string file in files)
		{
			if (Array.IndexOf(extensionsToInclude, Path.GetExtension(file)) != -1)
			{
				TextGroupFoundByWizard newTextGroup = new TextGroupFoundByWizard();
				newTextGroup.RelativePath = file.Replace(rootPath, "").Replace(Path.GetFileName(file), ""); //this replace is probably fairly shaky
				newTextGroup.RelativePathAndFileName = file.Replace(rootPath, "");
				newTextGroup.FileName = Path.GetFileName(file);
				newTextGroup.TextDefinitions = ExtractTextsFromFile(file, regexPattern);

				foundTextGroups.Add(newTextGroup);
			}
		}
		string[] folders = Directory.GetDirectories(baseFolder);
		foreach (string folder in folders)
		{
			foundTextGroups = ExtractTextsFromFiles(folder, extensionsToInclude, foundTextGroups, pathToSearch, regexPattern);
		}

		return foundTextGroups;
	}

}
